/*
	File                 : RandomValuesDialog.cpp
	Project              : LabPlot
	Description          : Dialog for generating non-uniformly distributed random numbers
	--------------------------------------------------------------------
	SPDX-FileCopyrightText: 2014-2023 Alexander Semke <alexander.semke@web.de>
	SPDX-FileCopyrightText: 2016-2024 Stefan Gerlach <stefan.gerlach@uni.kn>
	SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "RandomValuesDialog.h"
#include "backend/core/Settings.h"
#include "backend/core/column/Column.h"
#include "backend/lib/macros.h"
#include "backend/nsl/nsl_randist.h"
#include "backend/nsl/nsl_sf_stats.h"
#include "backend/spreadsheet/Spreadsheet.h"
#include "kdefrontend/GuiTools.h"

#include <KLocalizedString>
#include <KWindowConfig>

#include <QDialogButtonBox>
#include <QDir>
#include <QMessageBox>
#include <QPushButton>
#include <QStandardPaths>
#include <QWindow>

#include <cmath>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_rng.h>

/*!
	\class RandomValuesDialog
	\brief Dialog for generating non-uniform random numbers.

	\ingroup kdefrontend
 */

RandomValuesDialog::RandomValuesDialog(Spreadsheet* s, QWidget* parent)
	: QDialog(parent)
	, m_spreadsheet(s) {
	setWindowTitle(i18nc("@title:window", "Random Values"));

	auto* mainWidget = new QWidget(this);
	ui.setupUi(mainWidget);
	auto* layout = new QVBoxLayout(this);

	auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
	m_okButton = buttonBox->button(QDialogButtonBox::Ok);
	m_okButton->setDefault(true);
	m_okButton->setToolTip(i18n("Generate random values according to the selected distribution"));
	m_okButton->setText(i18n("&Generate"));

	connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &RandomValuesDialog::close);
	connect(buttonBox, &QDialogButtonBox::accepted, this, &RandomValuesDialog::accept);
	connect(buttonBox, &QDialogButtonBox::rejected, this, &RandomValuesDialog::reject);

	layout->addWidget(mainWidget);
	layout->addWidget(buttonBox);
	setLayout(layout);
	setAttribute(Qt::WA_DeleteOnClose);

	QVector<QPair<QString, int>> distros;
	for (int i = 0; i < NSL_SF_STATS_DISTRIBUTION_COUNT; i++)
		if (nsl_sf_stats_distribution_supports_RNG(nsl_sf_stats_distribution(i)))
			distros << QPair<QString, int>(i18n(nsl_sf_stats_distribution_name[i]), i);
	std::sort(std::begin(distros), std::end(distros));
	for (const auto& d : distros)
		ui.cbDistribution->addItem(d.first, d.second);
	const int defaultDist = (int)nsl_sf_stats_gaussian; // default dist

	ui.leParameter1->setClearButtonEnabled(true);
	ui.leParameter2->setClearButtonEnabled(true);
	ui.leParameter3->setClearButtonEnabled(true);
	ui.leSeed->setClearButtonEnabled(true);

	ui.leParameter1->setValidator(new QDoubleValidator(ui.leParameter1));
	ui.leParameter2->setValidator(new QDoubleValidator(ui.leParameter2));
	ui.leParameter3->setValidator(new QDoubleValidator(ui.leParameter3));
	ui.leSeed->setValidator(new QIntValidator(ui.leSeed));

	QString info = i18n(
		"Seed number to initialize the random number generator.\n"
		"If the generator is seeded with the same value, the same stream of random numbers will be generated by successive calls.\n"
		"If no value is specified, the current timestamp will be used to seed the generator.");
	ui.lSeed->setToolTip(info);
	ui.leSeed->setToolTip(info);

	info = i18n("Random number distributions as defined in <a href=\"https://www.gnu.org/software/gsl/doc/html/randist.html\">GSL documentation</a>");
	ui.cbDistribution->setWhatsThis(info);

	connect(ui.cbDistribution, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RandomValuesDialog::distributionChanged);
	connect(ui.leParameter1, &QLineEdit::textChanged, this, &RandomValuesDialog::checkValues);
	connect(ui.leParameter2, &QLineEdit::textChanged, this, &RandomValuesDialog::checkValues);
	connect(ui.leParameter3, &QLineEdit::textChanged, this, &RandomValuesDialog::checkValues);
	connect(buttonBox, &QDialogButtonBox::accepted, this, &RandomValuesDialog::generate);

	// restore saved settings if available
	create(); // ensure there's a window created
	const KConfigGroup conf = Settings::group(QStringLiteral("RandomValuesDialog"));

	const int dist = conf.readEntry("Distribution", defaultDist);
	ui.cbDistribution->setCurrentIndex(ui.cbDistribution->findData(dist));
	if (ui.cbDistribution->currentIndex() == 0) // if index=0 no signal is emitted above, call this slot directly
		this->distributionChanged();
	const auto numberLocale = QLocale();
	// read parameter or set values for default dist
	ui.leParameter1->setText(numberLocale.toString(conf.readEntry("Parameter1", 0.0)));
	ui.leParameter2->setText(numberLocale.toString(conf.readEntry("Parameter2", 1.0)));
	ui.leParameter3->setText(numberLocale.toString(conf.readEntry("Parameter3", 1.0)));
	ui.leSeed->setText(conf.readEntry("Seed", QString()));

	if (conf.exists()) {
		KWindowConfig::restoreWindowSize(windowHandle(), conf);
		resize(windowHandle()->size()); // workaround for QTBUG-40584
	} else {
		ui.cbDistribution->setCurrentIndex(ui.cbDistribution->findData(defaultDist));
		resize(QSize(400, 0).expandedTo(minimumSize()));
	}
}

RandomValuesDialog::~RandomValuesDialog() {
	// save current settings
	KConfigGroup conf = Settings::group(QStringLiteral("RandomValuesDialog"));
	// saving enum value to be consistent
	conf.writeEntry("Distribution", ui.cbDistribution->itemData(ui.cbDistribution->currentIndex()).toInt());
	const auto numberLocale = QLocale();
	conf.writeEntry("Parameter1", numberLocale.toDouble(ui.leParameter1->text()));
	conf.writeEntry("Parameter2", numberLocale.toDouble(ui.leParameter2->text()));
	conf.writeEntry("Parameter3", numberLocale.toDouble(ui.leParameter3->text()));
	conf.writeEntry("Seed", ui.leSeed->text());

	KWindowConfig::saveWindowSize(windowHandle(), conf);
}

void RandomValuesDialog::setColumns(const QVector<Column*>& columns) {
	m_columns = columns;
}

void RandomValuesDialog::distributionChanged(int index) {
	DEBUG(Q_FUNC_INFO << ", index = " << index)
	const auto dist = (nsl_sf_stats_distribution)ui.cbDistribution->itemData(index).toInt();
	DEBUG(Q_FUNC_INFO << ", dist = " << nsl_sf_stats_distribution_name[(int)dist])

	// default settings: show two parameter (used by most distributions)
	ui.lParameter1->show();
	ui.leParameter1->show();
	ui.lParameter2->show();
	ui.leParameter2->show();
	ui.lParameter3->hide();
	ui.leParameter3->hide();
	ui.lFunc->setText(QStringLiteral("p(x) ="));

	const auto numberLocale = QLocale();
	switch (dist) {
	case nsl_sf_stats_gaussian:
		ui.lParameter1->setText(UTF8_QSTRING("μ ="));
		ui.lParameter2->setText(UTF8_QSTRING("σ ="));
		ui.leParameter1->setText(numberLocale.toString(0.0));
		ui.leParameter2->setText(numberLocale.toString(1.0));
		break;
	case nsl_sf_stats_gaussian_tail:
		ui.lParameter3->show();
		ui.leParameter3->show();
		ui.lParameter1->setText(UTF8_QSTRING("μ ="));
		ui.lParameter2->setText(UTF8_QSTRING("σ ="));
		ui.lParameter3->setText(QStringLiteral("a ="));
		ui.leParameter1->setText(numberLocale.toString(0.0));
		ui.leParameter2->setText(numberLocale.toString(1.0));
		ui.leParameter3->setText(numberLocale.toString(0.0));
		break;
	case nsl_sf_stats_exponential:
		ui.lParameter1->setText(UTF8_QSTRING("λ ="));
		ui.lParameter2->setText(UTF8_QSTRING("μ ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		ui.leParameter2->setText(numberLocale.toString(0.0));
		break;
	case nsl_sf_stats_laplace:
		ui.lParameter1->setText(UTF8_QSTRING("μ ="));
		ui.lParameter2->setText(UTF8_QSTRING("σ ="));
		ui.leParameter1->setText(numberLocale.toString(0.0));
		ui.leParameter2->setText(numberLocale.toString(1.0));
		break;
	case nsl_sf_stats_exponential_power:
		ui.lParameter3->show();
		ui.leParameter3->show();
		ui.lParameter1->setText(UTF8_QSTRING("μ ="));
		ui.lParameter2->setText(UTF8_QSTRING("σ ="));
		ui.lParameter3->setText(QStringLiteral("b ="));
		ui.leParameter1->setText(numberLocale.toString(0.0));
		ui.leParameter2->setText(numberLocale.toString(1.0));
		ui.leParameter3->setText(numberLocale.toString(1.0));
		break;
	case nsl_sf_stats_cauchy_lorentz:
		ui.lParameter1->setText(UTF8_QSTRING("γ ="));
		ui.lParameter2->setText(UTF8_QSTRING("μ ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		ui.leParameter2->setText(numberLocale.toString(0.0));
		break;
	case nsl_sf_stats_rayleigh:
		ui.lParameter2->hide();
		ui.leParameter2->hide();
		ui.lParameter1->setText(UTF8_QSTRING("σ ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		break;
	case nsl_sf_stats_rayleigh_tail:
		ui.lParameter1->setText(UTF8_QSTRING("μ ="));
		ui.lParameter2->setText(UTF8_QSTRING("σ ="));
		ui.leParameter1->setText(numberLocale.toString(0.0));
		ui.leParameter2->setText(numberLocale.toString(1.0));
		break;
	case nsl_sf_stats_landau:
		ui.lParameter1->hide();
		ui.leParameter1->hide();
		ui.lParameter2->hide();
		ui.leParameter2->hide();
		break;
	case nsl_sf_stats_levy_alpha_stable:
		ui.lParameter1->setText(QStringLiteral("c ="));
		ui.lParameter2->setText(UTF8_QSTRING("α ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		ui.leParameter2->setText(numberLocale.toString(1.0));
		break;
	case nsl_sf_stats_levy_skew_alpha_stable:
		ui.lParameter3->show();
		ui.leParameter3->show();
		ui.lParameter1->setText(UTF8_QSTRING("c ="));
		ui.lParameter2->setText(UTF8_QSTRING("α ="));
		ui.lParameter3->setText(UTF8_QSTRING("β ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		ui.leParameter2->setText(numberLocale.toString(1.0));
		ui.leParameter3->setText(numberLocale.toString(1.0));
		break;
	case nsl_sf_stats_flat:
		ui.lParameter1->setText(QStringLiteral("a ="));
		ui.lParameter2->setText(QStringLiteral("b ="));
		ui.leParameter1->setText(numberLocale.toString(0.0));
		ui.leParameter2->setText(numberLocale.toString(1.0));
		break;
	case nsl_sf_stats_gamma:
		ui.lParameter1->setText(UTF8_QSTRING("θ ="));
		ui.lParameter2->setText(QStringLiteral("k ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		ui.leParameter2->setText(numberLocale.toString(1.0));
		break;
	case nsl_sf_stats_weibull:
		ui.lParameter3->show();
		ui.leParameter3->show();
		ui.lParameter1->setText(QStringLiteral("k ="));
		ui.lParameter2->setText(UTF8_QSTRING("λ ="));
		ui.lParameter3->setText(UTF8_QSTRING("μ ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		ui.leParameter2->setText(numberLocale.toString(1.0));
		ui.leParameter3->setText(numberLocale.toString(1.0));
		break;
	case nsl_sf_stats_beta:
		ui.lParameter1->setText(QStringLiteral("a ="));
		ui.lParameter2->setText(QStringLiteral("b ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		ui.leParameter2->setText(numberLocale.toString(1.0));
		break;
	case nsl_sf_stats_gumbel1:
		ui.lParameter3->show();
		ui.leParameter3->show();
		ui.lParameter1->setText(UTF8_QSTRING("σ ="));
		ui.lParameter2->setText(UTF8_QSTRING("β ="));
		ui.lParameter3->setText(UTF8_QSTRING("μ ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		ui.leParameter2->setText(numberLocale.toString(1.0));
		ui.leParameter3->setText(numberLocale.toString(0.0));
		break;
	case nsl_sf_stats_gumbel2:
		ui.lParameter3->show();
		ui.leParameter3->show();
		ui.lParameter1->setText(QStringLiteral("a ="));
		ui.lParameter2->setText(QStringLiteral("b ="));
		ui.lParameter3->setText(UTF8_QSTRING("μ ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		ui.leParameter2->setText(numberLocale.toString(1.0));
		ui.leParameter3->setText(numberLocale.toString(0.0));
		break;
	case nsl_sf_stats_pareto:
		ui.lParameter1->setText(QStringLiteral("a ="));
		ui.lParameter2->setText(QStringLiteral("b ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		ui.leParameter2->setText(numberLocale.toString(0.0));
		break;
	case nsl_sf_stats_lognormal:
		ui.lParameter1->setText(UTF8_QSTRING("μ ="));
		ui.lParameter2->setText(UTF8_QSTRING("σ ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		ui.leParameter2->setText(numberLocale.toString(1.0));
		break;
	case nsl_sf_stats_chi_squared:
		ui.lParameter2->hide();
		ui.leParameter2->hide();
		ui.lParameter1->setText(QStringLiteral("n ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		break;
	case nsl_sf_stats_fdist:
		ui.lParameter1->setText(UTF8_QSTRING("ν₁ ="));
		ui.lParameter2->setText(UTF8_QSTRING("ν₂ ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		ui.leParameter2->setText(numberLocale.toString(1.0));
		break;
	case nsl_sf_stats_tdist:
		ui.lParameter2->hide();
		ui.leParameter2->hide();
		ui.lParameter1->setText(UTF8_QSTRING("ν ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		ui.leParameter1->setText(QStringLiteral("1.0"));
		break;
	case nsl_sf_stats_logistic:
		ui.lParameter1->setText(UTF8_QSTRING("σ ="));
		ui.lParameter2->setText(UTF8_QSTRING("μ ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		ui.leParameter2->setText(numberLocale.toString(0.0));
		break;
	case nsl_sf_stats_poisson:
		ui.lParameter2->hide();
		ui.leParameter2->hide();
		ui.lFunc->setText(QStringLiteral("p(k) ="));
		ui.lParameter1->setText(UTF8_QSTRING("λ ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		ui.leParameter1->setText(QStringLiteral("1.0"));
		break;
	case nsl_sf_stats_bernoulli:
	case nsl_sf_stats_geometric:
	case nsl_sf_stats_logarithmic:
		ui.lParameter2->hide();
		ui.leParameter2->hide();
		if (dist == nsl_sf_stats_bernoulli)
			ui.lFunc->setText(QString());
		else
			ui.lFunc->setText(QStringLiteral("p(k) ="));
		ui.lParameter1->setText(QStringLiteral("p ="));
		ui.leParameter1->setText(numberLocale.toString(0.5));
		break;
	case nsl_sf_stats_binomial:
	case nsl_sf_stats_negative_binomial:
	case nsl_sf_stats_pascal:
		ui.lFunc->setText(QStringLiteral("p(k) ="));
		ui.lParameter1->setText(QStringLiteral("p ="));
		ui.lParameter2->setText(QStringLiteral("n ="));
		ui.leParameter1->setText(numberLocale.toString(0.5));
		ui.leParameter2->setText(numberLocale.toString(100));
		break;
	case nsl_sf_stats_hypergeometric:
		ui.lParameter3->show();
		ui.leParameter3->show();
		ui.lFunc->setText(QStringLiteral("p(k) ="));
		ui.lParameter1->setText(QStringLiteral("n1 ="));
		ui.lParameter2->setText(QStringLiteral("n2 ="));
		ui.lParameter3->setText(QStringLiteral("t ="));
		ui.leParameter1->setText(numberLocale.toString(1.0));
		ui.leParameter2->setText(numberLocale.toString(2.0));
		ui.leParameter3->setText(numberLocale.toString(3.0));
		break;
	case nsl_sf_stats_triangular:
		ui.lParameter3->show();
		ui.leParameter3->show();
		ui.lParameter1->setText(QStringLiteral("a ="));
		ui.lParameter2->setText(QStringLiteral("b ="));
		ui.lParameter3->setText(QStringLiteral("c ="));
		ui.leParameter1->setText(numberLocale.toString(0.0));
		ui.leParameter2->setText(numberLocale.toString(1.0));
		ui.leParameter3->setText(numberLocale.toString(0.5));
		break;
	case nsl_sf_stats_maxwell_boltzmann: // distributions not supporting RNG
	case nsl_sf_stats_sech:
	case nsl_sf_stats_levy:
	case nsl_sf_stats_frechet:
		break;
	}

	QString file =
		QStandardPaths::locate(QStandardPaths::AppDataLocation,
							   QStringLiteral("pics/gsl_distributions/") + QLatin1String(nsl_sf_stats_distribution_pic_name[dist]) + QStringLiteral(".pdf"));
	QImage image = GuiTools::importPDFFile(file);

	// use system palette for background
	if (GuiTools::isDarkMode()) {
		// invert image if in dark mode
		image.invertPixels();

		for (int i = 0; i < image.size().width(); i++)
			for (int j = 0; j < image.size().height(); j++)
				if (qGray(image.pixel(i, j)) < 64) // 0-255: 0-64 covers all dark pixel
					image.setPixel(QPoint(i, j), palette().color(QPalette::Base).rgb());
	} else {
		for (int i = 0; i < image.size().width(); i++)
			for (int j = 0; j < image.size().height(); j++)
				if (qGray(image.pixel(i, j)) > 192) // 0-255: 224-255 covers all light pixel
					image.setPixel(QPoint(i, j), palette().color(QPalette::Base).rgb());
	}

	if (image.isNull()) {
		ui.lFunc->hide();
		ui.lFuncPic->hide();
	} else {
		// use light/dark background in the preview label
		QPalette p;
		p.setColor(QPalette::Window, palette().color(QPalette::Base));
		ui.lFuncPic->setAutoFillBackground(true);
		ui.lFuncPic->setPalette(p);
		ui.lFuncPic->setScaledContents(false);

		ui.lFuncPic->setPixmap(QPixmap::fromImage(image));
		ui.lFuncPic->show();
	}
}

void RandomValuesDialog::checkValues() {
	if (ui.leParameter1->text().simplified().isEmpty()) {
		m_okButton->setEnabled(false);
		return;
	}

	if (ui.leParameter2->isVisible() && ui.leParameter2->text().simplified().isEmpty()) {
		m_okButton->setEnabled(false);
		return;
	}

	if (ui.leParameter3->isVisible() && ui.leParameter3->text().simplified().isEmpty()) {
		m_okButton->setEnabled(false);
		return;
	}

	m_okButton->setEnabled(true);
	return;
}

void RandomValuesDialog::generate() {
	Q_ASSERT(m_spreadsheet);

	WAIT_CURSOR;
	const int rows = m_spreadsheet->rowCount();
	QVector<double> data;
	QVector<int> data_int;
	QVector<qint64> data_bigint;
	try {
		data.resize(rows);
		data_int.resize(rows);
		data_bigint.resize(rows);
	} catch (std::bad_alloc&) {
		RESET_CURSOR;
		QMessageBox::critical(this, i18n("Failed to allocate memory"), i18n("Not enough memory to perform this operation."));
		return;
	}

	// create a generator chosen by the environment variable GSL_RNG_TYPE
	gsl_rng_env_setup();
	const gsl_rng_type* T = gsl_rng_default;
	gsl_rng* r = gsl_rng_alloc(T);

	ulong seed;
	if (!ui.leSeed->text().isEmpty()) {
		bool ok;
		seed = ui.leSeed->text().toULong(&ok);
		if (!ok)
			seed = QDateTime::currentMSecsSinceEpoch();
	} else
		seed = QDateTime::currentMSecsSinceEpoch();

	gsl_rng_set(r, seed);

	m_spreadsheet->beginMacro(
		i18np("%1: fill column with non-uniform random numbers", "%1: fill columns with non-uniform random numbers", m_spreadsheet->name(), m_columns.size()));

	for (auto* col : m_columns) {
		col->setSuppressDataChangedSignal(true);
		col->clearFormula(); // clear the potentially available column formula
	}

	const int index = ui.cbDistribution->currentIndex();
	const auto dist = (nsl_sf_stats_distribution)ui.cbDistribution->itemData(index).toInt();
	DEBUG(Q_FUNC_INFO << ", random number distribution: " << nsl_sf_stats_distribution_name[dist]);

	switch (dist) {
	case nsl_sf_stats_gaussian: {
		double mu{0.0}, sigma{1.0};
		SET_DOUBLE_FROM_LE(mu, ui.leParameter1)
		SET_DOUBLE_FROM_LE(sigma, ui.leParameter2)
		DEBUG(Q_FUNC_INFO << ", mu = " << mu << ", sigma = " << sigma);
		for (auto* col : m_columns) {
			auto mode = col->columnMode();
			if (mode == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_gaussian(r, sigma) + mu;
				col->setValues(data);
			} else if (mode == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_gaussian(r, sigma) + mu);
				col->setIntegers(data_int);
			} else if (mode == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_gaussian(r, sigma) + mu);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_gaussian_tail: {
		double mu{0.0}, sigma{1.0}, a{0.0};
		SET_DOUBLE_FROM_LE(mu, ui.leParameter1)
		SET_DOUBLE_FROM_LE(sigma, ui.leParameter2)
		SET_DOUBLE_FROM_LE(a, ui.leParameter3)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_gaussian_tail(r, a, sigma) + mu;
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_gaussian_tail(r, a, sigma) + mu);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_gaussian_tail(r, a, sigma) + mu);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_exponential: {
		double l{1.0}, mu{0.0};
		SET_DOUBLE_FROM_LE(l, ui.leParameter1)
		SET_DOUBLE_FROM_LE(mu, ui.leParameter2)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				// GSL uses the inverse for exp. distrib.
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_exponential(r, 1. / l) + mu;
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_exponential(r, 1. / l) + mu);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_exponential(r, 1. / l) + mu);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_laplace: {
		double mu{0.0}, s{1.0};
		SET_DOUBLE_FROM_LE(mu, ui.leParameter1)
		SET_DOUBLE_FROM_LE(s, ui.leParameter2)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_laplace(r, s) + mu;
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_laplace(r, s) + mu);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_laplace(r, s) + mu);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_exponential_power: {
		double mu{0.0}, a{1.0}, b{1.0};
		SET_DOUBLE_FROM_LE(mu, ui.leParameter1)
		SET_DOUBLE_FROM_LE(a, ui.leParameter2)
		SET_DOUBLE_FROM_LE(b, ui.leParameter3)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_exppow(r, a, b) + mu;
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_exppow(r, a, b) + mu);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_exppow(r, a, b) + mu);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_cauchy_lorentz: {
		double gamma{1.0}, mu{0.0};
		SET_DOUBLE_FROM_LE(gamma, ui.leParameter1)
		SET_DOUBLE_FROM_LE(mu, ui.leParameter2)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_cauchy(r, gamma) + mu;
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_cauchy(r, gamma) + mu);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_cauchy(r, gamma) + mu);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_rayleigh: {
		double s{1.0};
		SET_DOUBLE_FROM_LE(s, ui.leParameter1)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_rayleigh(r, s);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_rayleigh(r, s));
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_rayleigh(r, s));
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_rayleigh_tail: {
		double mu{0.0}, sigma{1.0};
		SET_DOUBLE_FROM_LE(mu, ui.leParameter1)
		SET_DOUBLE_FROM_LE(sigma, ui.leParameter2)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_rayleigh_tail(r, mu, sigma);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_rayleigh_tail(r, mu, sigma));
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_rayleigh_tail(r, mu, sigma));
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_landau:
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_landau(r);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_landau(r));
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_landau(r));
				col->setBigInts(data_bigint);
			}
		}
		break;
	case nsl_sf_stats_levy_alpha_stable: {
		double c{1.0}, alpha{1.0};
		SET_DOUBLE_FROM_LE(c, ui.leParameter1)
		SET_DOUBLE_FROM_LE(alpha, ui.leParameter2)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_levy(r, c, alpha);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_levy(r, c, alpha));
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_levy(r, c, alpha));
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_levy_skew_alpha_stable: {
		double c{1.0}, alpha{1.0}, beta{1.0};
		SET_DOUBLE_FROM_LE(c, ui.leParameter1)
		SET_DOUBLE_FROM_LE(alpha, ui.leParameter2)
		SET_DOUBLE_FROM_LE(beta, ui.leParameter3)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_levy_skew(r, c, alpha, beta);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_levy_skew(r, c, alpha, beta));
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_levy_skew(r, c, alpha, beta));
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_flat: {
		double a{0.0}, b{1.0};
		SET_DOUBLE_FROM_LE(a, ui.leParameter1)
		SET_DOUBLE_FROM_LE(b, ui.leParameter2)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_flat(r, a, b);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_flat(r, a, b));
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_flat(r, a, b));
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_gamma: {
		double a{1.0}, b{1.0};
		SET_DOUBLE_FROM_LE(a, ui.leParameter1)
		SET_DOUBLE_FROM_LE(b, ui.leParameter2)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_gamma(r, a, b);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_gamma(r, a, b));
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_gamma(r, a, b));
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_lognormal: {
		double s{1.0}, mu{1.0};
		SET_DOUBLE_FROM_LE(mu, ui.leParameter1)
		SET_DOUBLE_FROM_LE(s, ui.leParameter2)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_lognormal(r, mu, s);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_lognormal(r, mu, s));
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_lognormal(r, mu, s));
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_chi_squared: {
		double n{1.0};
		SET_DOUBLE_FROM_LE(n, ui.leParameter1)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_chisq(r, n);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_chisq(r, n));
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_chisq(r, n));
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_fdist: {
		double nu1{1.0}, nu2{1.0};
		SET_DOUBLE_FROM_LE(nu1, ui.leParameter1)
		SET_DOUBLE_FROM_LE(nu2, ui.leParameter2)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_fdist(r, nu1, nu2);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_fdist(r, nu1, nu2));
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_fdist(r, nu1, nu2));
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_tdist: {
		double nu{1.0};
		SET_DOUBLE_FROM_LE(nu, ui.leParameter1)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_tdist(r, nu);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_tdist(r, nu));
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_tdist(r, nu));
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_beta: {
		double a{1.0}, b{1.0};
		SET_DOUBLE_FROM_LE(a, ui.leParameter1)
		SET_DOUBLE_FROM_LE(b, ui.leParameter2)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_beta(r, a, b);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_beta(r, a, b));
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_beta(r, a, b));
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_logistic: {
		double s{1.0}, mu{0.0};
		SET_DOUBLE_FROM_LE(s, ui.leParameter1)
		SET_DOUBLE_FROM_LE(mu, ui.leParameter2)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_logistic(r, s) + mu;
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_logistic(r, s) + mu);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_logistic(r, s) + mu);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_pareto: {
		double a{1.0}, b{0.0};
		SET_DOUBLE_FROM_LE(a, ui.leParameter1)
		SET_DOUBLE_FROM_LE(b, ui.leParameter2)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_pareto(r, a, b);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_pareto(r, a, b));
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_pareto(r, a, b));
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_weibull: {
		double k{1.0}, l{1.0}, mu{1.0};
		SET_DOUBLE_FROM_LE(k, ui.leParameter1)
		SET_DOUBLE_FROM_LE(l, ui.leParameter2)
		SET_DOUBLE_FROM_LE(mu, ui.leParameter3)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_weibull(r, l, k) + mu;
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_weibull(r, l, k) + mu);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_weibull(r, l, k) + mu);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_gumbel1: {
		double s{1.0}, b{1.0}, mu{0.0};
		SET_DOUBLE_FROM_LE(s, ui.leParameter1)
		SET_DOUBLE_FROM_LE(b, ui.leParameter2)
		SET_DOUBLE_FROM_LE(mu, ui.leParameter3)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_gumbel1(r, 1. / s, b) + mu;
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_gumbel1(r, 1. / s, b) + mu);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_gumbel1(r, 1. / s, b) + mu);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_gumbel2: {
		double a{1.0}, b{1.0}, mu{0.0};
		SET_DOUBLE_FROM_LE(a, ui.leParameter1)
		SET_DOUBLE_FROM_LE(b, ui.leParameter2)
		SET_DOUBLE_FROM_LE(mu, ui.leParameter3)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = gsl_ran_gumbel2(r, a, b) + mu;
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = std::round(gsl_ran_gumbel2(r, a, b) + mu);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = std::round(gsl_ran_gumbel2(r, a, b) + mu);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_poisson: {
		double l{1.0};
		SET_DOUBLE_FROM_LE(l, ui.leParameter1)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = (double)gsl_ran_poisson(r, l);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = gsl_ran_poisson(r, l);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = (qint64)gsl_ran_poisson(r, l);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_bernoulli: {
		double p{1.0};
		SET_DOUBLE_FROM_LE(p, ui.leParameter1)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = (double)gsl_ran_bernoulli(r, p);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = gsl_ran_bernoulli(r, p);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = (qint64)gsl_ran_bernoulli(r, p);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_binomial: {
		double p{0.5}, n{100.};
		SET_DOUBLE_FROM_LE(p, ui.leParameter1)
		SET_DOUBLE_FROM_LE(n, ui.leParameter2)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = (double)gsl_ran_binomial(r, p, n);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = gsl_ran_binomial(r, p, n);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = (qint64)gsl_ran_binomial(r, p, n);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_negative_binomial: {
		double p{0.5}, n{100.};
		SET_DOUBLE_FROM_LE(p, ui.leParameter1)
		SET_DOUBLE_FROM_LE(n, ui.leParameter2)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = (double)gsl_ran_negative_binomial(r, p, n);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = gsl_ran_negative_binomial(r, p, n);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = (qint64)gsl_ran_negative_binomial(r, p, n);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_pascal: {
		double p{0.5}, n{100.};
		SET_DOUBLE_FROM_LE(p, ui.leParameter1)
		SET_DOUBLE_FROM_LE(n, ui.leParameter2)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = (double)gsl_ran_pascal(r, p, n);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = gsl_ran_pascal(r, p, n);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = (qint64)gsl_ran_pascal(r, p, n);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_geometric: {
		double p{0.5};
		SET_DOUBLE_FROM_LE(p, ui.leParameter1)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = (double)gsl_ran_geometric(r, p);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = gsl_ran_geometric(r, p);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = (qint64)gsl_ran_geometric(r, p);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_hypergeometric: {
		double n1{1.0}, n2{2.0}, t{3.0};
		SET_DOUBLE_FROM_LE(n1, ui.leParameter1)
		SET_DOUBLE_FROM_LE(n2, ui.leParameter2)
		SET_DOUBLE_FROM_LE(t, ui.leParameter3)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = (double)gsl_ran_hypergeometric(r, n1, n2, t);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = gsl_ran_hypergeometric(r, n1, n2, t);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = (qint64)gsl_ran_hypergeometric(r, n1, n2, t);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_logarithmic: {
		double p{0.5};
		SET_DOUBLE_FROM_LE(p, ui.leParameter1)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = (double)gsl_ran_logarithmic(r, p);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = gsl_ran_logarithmic(r, p);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = (qint64)gsl_ran_logarithmic(r, p);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	case nsl_sf_stats_triangular: {
		double a{0.0}, b{1.0}, c{0.5};
		SET_DOUBLE_FROM_LE(a, ui.leParameter1)
		SET_DOUBLE_FROM_LE(b, ui.leParameter2)
		SET_DOUBLE_FROM_LE(c, ui.leParameter3)
		for (auto* col : m_columns) {
			if (col->columnMode() == AbstractColumn::ColumnMode::Double) {
				for (int i = 0; i < rows; ++i)
					data[i] = nsl_ran_triangular(r, a, b, c);
				col->setValues(data);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::Integer) {
				for (int i = 0; i < rows; ++i)
					data_int[i] = (int)nsl_ran_triangular(r, a, b, c);
				col->setIntegers(data_int);
			} else if (col->columnMode() == AbstractColumn::ColumnMode::BigInt) {
				for (int i = 0; i < rows; ++i)
					data_bigint[i] = (qint64)nsl_ran_triangular(r, a, b, c);
				col->setBigInts(data_bigint);
			}
		}
		break;
	}
	// distributions without RNG
	case nsl_sf_stats_maxwell_boltzmann:
	case nsl_sf_stats_sech:
	case nsl_sf_stats_levy:
	case nsl_sf_stats_frechet:
		break;
	}

	for (auto* col : m_columns) {
		col->setSuppressDataChangedSignal(false);
		col->setChanged();
	}
	m_spreadsheet->endMacro();
	RESET_CURSOR;

	gsl_rng_free(r);
}
